/*
 * Copyright (c) 2013, Klaus Hauschild
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided
 * that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice, this list of conditions and
 *     the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
 *     and the following disclaimer in the documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package de.hauschild.utilsn.java;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;

import org.lwjgl.opengl.Display;

import de.hauschild.utilsn.java.PNGDecoder.Format;

/**
 * Utilities for and better use of {@link playn.java.JavaPlatform JavaPlatform}.
 * 
 * @since 0.1.0
 * 
 * @author Klaus Hauschild
 */
public abstract class JavaUtilsN {

  /**
   * Sets the icon(s) of the Java application. The count and size of the icons depends on on the hosting os. For more
   * details see {@link Display#setIcon(ByteBuffer[])}.
   * <p>
   * The icons has to be located in class path at <code>assets/icons/</code>. The icons has to be in <code>PNG</code>
   * format.
   * </p>
   * 
   * @param icons
   * 
   * @see Display#setIcon(ByteBuffer[])
   */
  public static void setIcons(final String[] icons) {
    final ByteBuffer[] byteBuffers = new ByteBuffer[icons.length];
    for (int i = 0; i < icons.length; i++) {
      byteBuffers[i] = convert(ClassLoader.getSystemResourceAsStream("assets/icons/" + icons[i]));
    }
    try {
      final Field cached_icons = Display.class.getDeclaredField("cached_icons");
      setFinalStatic(cached_icons, byteBuffers);
    } catch (final Exception exception) {
    }
  }

  /**
   * Set up the environment property 'java.library.path' to the given path.
   * 
   * @param nativesPath
   *          path to native libraries
   */
  public static void setLibraryPath(final File nativesPath) {
    System.setProperty("java.library.path", nativesPath.getAbsolutePath());
    try {
      final Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
      fieldSysPath.setAccessible(true);
      fieldSysPath.set(null, null);
    } catch (final Exception exception) {
      throw new RuntimeException("Unable to re-set [java.library.path].", exception);
    }
  }

  /**
   * Set up the environment property 'java.library.path' automatically. It is assumed that all native libraries stored
   * in a folder 'natives'. This folder has to be next to or 'under' (in the folder tree) the execution path.
   * <p>
   * If you use a different setting use {@link #setLibraryPath(File)} direct.
   * </p>
   */
  public static void setupNatives() {
    final File nativesPath = searchNativesPath();
    if (nativesPath == null) {
      throw new RuntimeException("Unable to locate native libraries.");
    }
    setLibraryPath(nativesPath);
  }

  private static ByteBuffer convert(final InputStream imageInputStream) {
    try {
      final PNGDecoder decoder = new PNGDecoder(imageInputStream);
      final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(4 * decoder.getWidth() * decoder.getHeight());
      decoder.decode(byteBuffer, decoder.getWidth() * 4, Format.RGBA);
      byteBuffer.flip();
      return byteBuffer;
    } catch (final Exception exception) {
      return null;
    } finally {
      try {
        imageInputStream.close();
      } catch (final IOException e) {
      }
    }
  }

  private static File searchNativesPath() {
    return searchNativesPath(new File("."));
  }

  private static File searchNativesPath(final File file) {
    for (final File child : file.listFiles()) {
      if ("natives".equals(child.getName())) {
        return child;
      }
      if (child.isDirectory()) {
        final File nativesPath = searchNativesPath(child);
        if (nativesPath != null) {
          return nativesPath;
        }
      }
    }
    return null;
  }

  private static void setFinalStatic(final Field field, final Object newValue) throws Exception {
    field.setAccessible(true);
    final Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    field.set(null, newValue);
  }

}
